package com.tally.business.controller;

import com.tally.business.mq.MqProducer;
import com.tally.server.config.ConstKey;
import com.tally.server.dto.EverydayIncomeDTO;
import com.tally.server.dto.IncomeBatchDTO;
import com.tally.server.dto.IncomeDTO;
import com.tally.server.dto.ResponseDTO;
import com.tally.server.enums.ExpTypeEnum;
import com.tally.server.exception.ValidatorException;
import com.tally.server.service.IncomeService;
import com.tally.server.utils.DateUtil;
import com.tally.server.utils.ValidatorUtil;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author: sy
 * @Date: 2020/11/13 20:13
 * @Description:
 */
@RestController
@RequestMapping("/income")
public class IncomeController extends BusinessController {

    private static final Logger logger = LoggerFactory.getLogger(IncomeController.class);
    public static final String BUSINESS_NAME = "收入";

    @Autowired
    private IncomeService incomeService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private MqProducer mqProducer;

    @Value("${key.expire-time.graph}")
    private Long graphExpireTime;

    /**
     * 保存，id有值时更新，无值时新增
     * @param incomeDTO
     * @return
     */
    @PostMapping("/save")
    public ResponseDTO save(@RequestBody IncomeDTO incomeDTO){
        Integer userId;
        Date recordDate;
        // 保存校验
        ValidatorUtil.require(userId = incomeDTO.getUserId(),"用户id");
        ValidatorUtil.require(recordDate = incomeDTO.getRecordDate(),"日期");
        ValidatorUtil.require(incomeDTO.getIncomeType(),"收入类型");
        ValidatorUtil.require(incomeDTO.getMoney(),"金额");

        delKeys(userId,recordDate);

        int type = incomeService.save(incomeDTO);

        if(type == 1){
            mqProducer.addExp(userId, ExpTypeEnum.EXP_BUSINESS.getCode());
        }
        return ResponseDTO.success(incomeDTO);
    }

    /**
     * 批量保存
     * @param incomeBatchDTO
     * @return
     */
    @PostMapping("/save-batch")
    public ResponseDTO saveBatch(@RequestBody IncomeBatchDTO incomeBatchDTO) {
        Integer userId;
        Date recordStartDate, recordEndDate;
        // 保存校验
        ValidatorUtil.require(userId = incomeBatchDTO.getUserId(),"用户id");
        ValidatorUtil.require(recordStartDate = incomeBatchDTO.getRecordStartDate(),"开始日期");
        ValidatorUtil.require(recordEndDate = incomeBatchDTO.getRecordEndDate(),"结束日期");
        ValidatorUtil.require(incomeBatchDTO.getIncomeType(),"收入类型");
        ValidatorUtil.require(incomeBatchDTO.getMoney(), "金额");

        incomeService.saveBatch(incomeBatchDTO);
        Set<Date> dateSet = DateUtil.dateMonthList(recordStartDate, recordEndDate);
        for (Date recordDate: dateSet) {
            delKeys(userId, recordDate);
        }
        return ResponseDTO.success(null);
    }

    /**
     * 删除
     * @param id
     * @return
     */
    @DeleteMapping("/delete/{id}")
    @Transactional(rollbackFor = Exception.class)
    public ResponseDTO delete(@PathVariable String id){

        IncomeDTO incomeDTO = incomeService.selectById(id);
        // --- 异步
        delKeys(incomeDTO.getUserId(),incomeDTO.getRecordDate());
        // ---

        incomeService.delete(id);
        return ResponseDTO.success(null);
    }

    /**
     * 得到某年某月的收入列表
     * @param year
     * @param month
     * @param userId
     * @return
     */
    @GetMapping("/get-month-income/{year}/{month}/{userId}")
    public ResponseDTO getMonthIncome(@PathVariable String year, @PathVariable String month, @PathVariable String userId){
        if(StringUtils.isEmpty(year) || StringUtils.isEmpty(month)){
            return ResponseDTO.success(null);
        }
        int yearI = parseInt(year);
        int monthI = parseInt(month);
        List<IncomeDTO> incomeDTOList = incomeService.getMonthIncome(yearI,monthI,userId,true);
        return ResponseDTO.success(incomeDTOList);
    }

    /**
     * 得到某年的总支出金额
     * @param year
     * @param userId
     * @return
     */
    @GetMapping("/get-income-sum/{year}/{userId}")
    public ResponseDTO getIncomeSum(@PathVariable String year, @PathVariable String userId){
        if(StringUtils.isEmpty(year)){
            return ResponseDTO.success(BigDecimal.ZERO);
        }
        int yearI = parseInt(year);
        BigDecimal sum = incomeService.getYearSum(yearI, userId);
        return ResponseDTO.success(sum);
    }

    /**
     * 得到某年某月的总支出金额
     * @param year
     * @param month
     * @param userId
     * @return
     */
    @GetMapping("/get-income-sum/{year}/{month}/{userId}")
    public ResponseDTO getIncomeSum(@PathVariable String year, @PathVariable String month, @PathVariable String userId){
        int yearI = parseInt(year);
        int monthI = parseInt(month);
        BigDecimal sum = incomeService.getMonthSum(yearI, monthI, userId);
        return ResponseDTO.success(sum);
    }

    /**
     * 某月每天的收入总和
     * @param year
     * @param month
     * @param userId
     * @return [0,100,23.3,0,45,0...]
     */
    @GetMapping("/everyday-income-sum/{year}/{month}/{userId}")
    public ResponseDTO everydayIncomeSum(@PathVariable String year, @PathVariable String month, @PathVariable String userId){
        if(StringUtils.isEmpty(year) || StringUtils.isEmpty(month)){
            throw new ValidatorException("参数出现空值");
        }
        // 先查redis，若不存在key则查询数据库
        ListOperations listOperations = redisTemplate.opsForList();
        String listKey = generateKey("s s s s",ConstKey.GRAPH_INCOME_LINE_PREFIX,userId,year,month);
        if(redisTemplate.hasKey(listKey)){
            List<Integer> listRedis = listOperations.range(listKey, 0, listOperations.size(listKey));
            return ResponseDTO.success(listRedis);
        }

        int yearI = parseInt(year);
        int monthI = parseInt(month);
        List<BigDecimal> list = incomeService.everydayIncomeSum(yearI,monthI,userId);
        // 查完数据库后将数据写到redis，关联用户id，并设置过期时间，如：graph-income-line-{userId}-{year}-{month}, 30min
        listOperations.rightPushAll(listKey,list);
        redisTemplate.expire(listKey,graphExpireTime, TimeUnit.SECONDS);
        return ResponseDTO.success(list);
    }

    /**
     * 得到某年某月各项收入之和
     * @param year
     * @param month
     * @param userId
     * @return
     */
    @GetMapping("/each-income-sum/{year}/{month}/{userId}")
    public ResponseDTO eachIncomeSum(@PathVariable String year,@PathVariable String month,@PathVariable String userId){
        if(StringUtils.isEmpty(year) || StringUtils.isEmpty(month)){
            throw new ValidatorException("参数出现空值");
        }
        // 查询redis
        HashOperations hashOperations = redisTemplate.opsForHash();
        String hashKey = generateKey("s s s s",ConstKey.GRAPH_INCOME_PIE_PREFIX,userId,year,month);
        if(redisTemplate.hasKey(hashKey)){
            Map<String,BigDecimal> mapRedis = hashOperations.entries(hashKey);
            return ResponseDTO.success(mapRedis);
        }
        int yearI = parseInt(year);
        int monthI = parseInt(month);
        Map<String,BigDecimal> map = incomeService.eachIncomeSum(yearI,monthI,userId);
        // 写到redis
        hashOperations.putAll(hashKey,map);
        redisTemplate.expire(hashKey,graphExpireTime, TimeUnit.SECONDS);
        return ResponseDTO.success(map);
    }

    /**
     * 得到某年各项收入之和
     * @param year
     * @param userId
     * @return
     */
    @GetMapping("/each-income-sum/{year}/{userId}")
    public ResponseDTO eachIncomeSum(@PathVariable String year,@PathVariable String userId){
        if(StringUtils.isEmpty(year)){
            throw new ValidatorException("参数出现空值");
        }
        // 查询redis
        String hashKey = generateKey("s s s",ConstKey.GRAPH_INCOME_PIE_PREFIX,userId,year);
        HashOperations hashOperations = redisTemplate.opsForHash();
        if(redisTemplate.hasKey(hashKey)){
            Map<String,BigDecimal> mapRedis = hashOperations.entries(hashKey);
            return ResponseDTO.success(mapRedis);
        }
        int yearI = parseInt(year);
        Map<String,BigDecimal> map = incomeService.eachIncomeSum(yearI,userId);
        // 写到redis
        hashOperations.putAll(hashKey,map);
        redisTemplate.expire(hashKey,graphExpireTime, TimeUnit.SECONDS);
        return ResponseDTO.success(map);
    }

    /**
     * 得到某年每月收入总和
     * @param year
     * @param userId
     * @return
     */
    @GetMapping("/month-income-sum/{year}/{userId}")
    public ResponseDTO monthIncomeSum(@PathVariable String year, @PathVariable String userId){
        if(StringUtils.isEmpty(year)){
            throw new ValidatorException("参数出现空值");
        }
        // 先查redis，若不存在key则查询数据库
        ListOperations listOperations = redisTemplate.opsForList();
        String listKey = generateKey("s s s",ConstKey.GRAPH_INCOME_BAR_PREFIX,userId,year);
        if(redisTemplate.hasKey(listKey)){
            List<Integer> listRedis = listOperations.range(listKey, 0, listOperations.size(listKey));
            return ResponseDTO.success(listRedis);
        }
        int yearI = parseInt(year);
        List<BigDecimal> sumList = incomeService.monthIncomeSum(yearI,userId);
        // 写到redis
        listOperations.rightPushAll(listKey,sumList);
        redisTemplate.expire(listKey,graphExpireTime, TimeUnit.SECONDS);
        return ResponseDTO.success(sumList);
    }

    @GetMapping("/view")
    public ResponseDTO viewIncome(@RequestParam("start") String start,
                                  @RequestParam("end") String end) throws ParseException {
        Date startDate = DateUtils.parseDate(start, "yyyy-MM-dd");
        Date endDate = DateUtils.parseDate(end, "yyyy-MM-dd");
        Integer userId = getLoginUser().getInteger("id");
        List<EverydayIncomeDTO> everydayIncomeDTOList = incomeService.getEverydayList(startDate, endDate, userId);
        return ResponseDTO.success(everydayIncomeDTOList);
    }

    private void delKeys(Integer userId, Date recordDate) {
        int year = getYearAndMonth(recordDate)[0];
        int month = getYearAndMonth(recordDate)[1];
        String key1 = generateKey("s * s s s", ConstKey.GRAPH_INCOME_PREFIX,userId.toString(),String.valueOf(year),String.valueOf(month));
        String key2 = generateKey("s * s s",ConstKey.GRAPH_INCOME_PREFIX,userId.toString(),String.valueOf(year));
        String[] keys = new String[]{key1,key2};
        for(String key:keys){
            logger.info("key={}",key);
            Set keySet = redisTemplate.keys(key);
            redisTemplate.delete(keySet);
        }
    }
}
